package com.xiang.atomikos.datasources.aspect;

import com.xiang.atomikos.annotation.DataSource;
import com.xiang.atomikos.datasources.DataSourceNames;
import com.xiang.atomikos.datasources.DynamicDataSource;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

/**
 * 多数据源，切面处理类
 */
@Aspect
@Configuration
@Order(0)
@Slf4j
public class DataSourceAspect {

  @Pointcut("execution(* com.xiang.atomikos.service.impl.*.*(..)) && @target(com.xiang.atomikos.annotation.DataSource)")
  public void dataSourcePointCut() {

  }

  @Around("dataSourcePointCut()")
  public Object around(ProceedingJoinPoint point) throws Throwable {
    DataSource ds = null;
    Class<?> target = point.getTarget().getClass();
    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    ds = resolveDataSource(target, method);
    if (ds == null) {
      DynamicDataSource.setDataSource(DataSourceNames.DB1);
      log.info("set default datasource is " + DataSourceNames.DB1);
    } else {
      DynamicDataSource.setDataSource(ds.name());
      log.info("set datasource is " + ds.name());
    }
    return point.proceed();
  }

  @After("dataSourcePointCut()")
  public void restoreDataSource(JoinPoint point) {
    DynamicDataSource.clearDataSource();
  }

  /*
   * 获取最终的dataSource
   *
   * @param clazz
   * @param method
   */
  private DataSource resolveDataSource(Class<?> clazz, Method method) {
    try {
      DataSource ds = null;
      Class<?>[] types = method.getParameterTypes();
      // 默认使用类型注解
      if (clazz.isAnnotationPresent(DataSource.class)) {
        ds = clazz.getAnnotation(DataSource.class);
      }
      // 方法注解可以覆盖类型注解
      Method m = clazz.getMethod(method.getName(), types);
      if (m != null && m.isAnnotationPresent(DataSource.class)) {
        ds = m.getAnnotation(DataSource.class);
      }
      return ds;
    } catch (Exception e) {
      log.error(clazz + ":" + e.getMessage());
    }
    return null;
  }
}
